﻿using FileManagerSystem.DbInfos;
using FileManagerSystem.Extend;
using FileManagerSystem.Helper;
using FileManagerSystem.Models;
using FileManagerSystem.Service;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace FileManagerSystem.Controllers
{
    /// <summary>
    /// 本地文件管理器，本地文件上传下载请用此Controller接口
    /// </summary>
    public class LocalFileManagerController : ApiBaseController
    {
        int md5len = 1024 * 1024;

        /// <summary>
        /// 文件Service对象
        /// </summary>
        [Autowired]
        private IFileService _fileService { get; set; }

        /// <summary>
        /// 映射文件Service对象
        /// </summary>
        [Autowired]
        private IReflectFileService _reflectfileService { get; set; }

        /// <summary>
        /// 目录Service对象
        /// </summary>
        [Autowired]
        private IDirectoryService _directoryService { get; set; }

        /// <summary>
        /// 将本地文件归档到系统中（多文件）
        /// </summary>
        /// <param name="view"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<IActionResult> FilingLocalFileToSystem(FilingLocalFileView view)
        {
            var result = new List<FileView>();
            //解析路径获取文件名
            foreach (var filePath in view.FilePaths)
            {
                //如果文件存在，则归档到数据库中 归档操作为：拷贝文件+写入数据库数据
                if (FileOperateHelper.CheckFileExist(filePath))
                {
                    var fileView = new FileView();

                    FileInfo fileInfo = new FileInfo(filePath);
                    fileView.Name = fileInfo.FullName;
                    var fileMd5 = FileOperateHelper.GetMD5HashFromFile(filePath, md5len);//文件第一个切片的md5

                    var theSourceFile = await _fileService.FindOne(x => x.Md5 == fileMd5 && x.FileSize == fileInfo.Length);
                    if (theSourceFile != null)//找到相同的原始文件，进入秒传业务
                    {
                        //向映射文件存储一份文件信息拷贝
                        var newReflectFileGUID = await _reflectfileService.Insert(new ReflectFileInf
                        {
                            PId = theSourceFile.Id,
                            Name = Path.GetFileNameWithoutExtension(fileInfo.Name),
                            Extension = fileInfo.Extension,
                            AbstractDirPath = view.AbstractDirPath,
                            AbstractTopDirGuid = view.AbstractTopDirGuid,
                            Keep = false,
                        });
                        fileView.Guid = newReflectFileGUID.AsGuid.ToString();
                        fileView.Path = FileOperateHelper.GetPhysicPath(Path.Combine(theSourceFile.DirPath, theSourceFile.Id.ToString() + theSourceFile.Extension));
                    }
                    else
                    {
                        var yearMonth = DateTime.Now.ToString("yyyyMMdd");

                        //最终保存完整合并文件的目录
                        var finalDir = Path.Combine("StaticFiles", "Files", yearMonth);
                        //判断文件夹是否存在，不存在则新建文件夹
                        if (!FileOperateHelper.CreateFileDir(finalDir))
                            throw new Exception($"路径：{finalDir}，创建失败！");

                        //保存到真实目录时的文件名
                        var newFile = new FileInf
                        {
                            Name = Path.GetFileNameWithoutExtension(fileInfo.Name),
                            Extension = fileInfo.Extension,
                            MimeType = FileOperateHelper.GetMimeType(fileInfo.Extension),
                            Md5 = fileMd5,
                            FileSize = fileInfo.Length,
                            Keep = false,
                        };
                        var newFileGUID = await _fileService.Insert(newFile);

                        newFile.Id = newFileGUID;
                        //保存到真实目录时的文件名
                        string saveName = newFileGUID.AsGuid.ToString() + fileInfo.Extension;
                        //最终保存完整合并文件的路径（含文件名）
                        newFile.DirPath = finalDir;
                        await _fileService.Update(newFile);

                        //向映射文件存储一份文件信息拷贝
                        var newReflectFileGUID = await _reflectfileService.Insert(new ReflectFileInf
                        {
                            PId = newFileGUID,
                            Name = Path.GetFileNameWithoutExtension(fileInfo.Name),
                            Extension = fileInfo.Extension,
                            AbstractDirPath = view.AbstractDirPath,
                            AbstractTopDirGuid = view.AbstractTopDirGuid,
                            Keep = false,
                        });

                        //拷贝文件到存档目录中
                        var finalPath = Path.Combine(finalDir, saveName);
                        FileOperateHelper.CopyFile(filePath, finalPath, false, true);

                        fileView.Guid = newReflectFileGUID.AsGuid.ToString();
                        fileView.Path =FileOperateHelper.GetPhysicPath(Path.Combine(newFile.DirPath, newFile.Id.ToString() + newFile.Extension));
                    }
                    fileView.Path.Replace(@"\", "/");
                    result.Add(fileView);
                }
            }
            return Json(result);
        }

        ///// <summary>
        ///// 测试md5获取功能
        ///// </summary>
        ///// <param name="path"></param>
        ///// <returns></returns>
        //[HttpGet]
        //public async Task<IActionResult> GetMd5Test(string path)
        //{
        //    return Json(FileOperateHelper.GetMD5HashFromFile(path, md5len));
        //}

        /// <summary>
        /// 拉取指定Guid目录下所有文件信息（含路径）
        /// </summary>
        /// <param name="dirGuid">文件夹guid</param>
        /// <returns></returns>
        [HttpGet]
        public async Task<IActionResult> GetFilesInDirectoy(string dirGuid)
        {
            if (dirGuid.IsNullOrWhiteSpace() || !Guid.TryParse(dirGuid, out Guid id))
                throw new Exception($"传入的guid不合法：{dirGuid}");

            var tempGuid = Guid.NewGuid().ToString();
            var tempReadingDir = Path.Combine("StaticFiles", "TempReadFiles", tempGuid);


            try
            {
                //1.(创建临时文件夹)
                //  生成随机目录（用于临时打包输出）
                FileOperateHelper.CreateFileDir(tempReadingDir);

                var dirInf = await _directoryService.FindOne(x => x.Id == id);
                if (dirInf == null)
                    throw new Exception($"未找到目录");

                //2.在临时目录下创建指定顶级文件夹，并拉取顶级文件夹应有的文件，同时改名
                var topDirPath = Path.Combine(tempReadingDir, dirInf.AbstractDirPath);

                if (FileOperateHelper.CreateFileDir(topDirPath))
                {
                    var dirFiles = await GetAllFileInDir(dirGuid);

                    /*
                     * 创建文件夹 + 移动文件 + 重命名文件
                     */
                    foreach (var dirFile in dirFiles)
                    {
                        //处理文件虚拟路径(文件在目录中，不包含文件名)
                        dirFile.AbstractDirPath = dirFile.AbstractDirPath.Trim().Replace("/", @"\").Replace(@"\\", @"\");//将正斜杠全部替换为反斜杠
                        var dirFileAbstractPath = dirFile.AbstractDirPath.Split(@"\");
                        var topDirPaths = topDirPath.Split(@"\");
                        if (topDirPaths[topDirPaths.Length - 2] == dirFileAbstractPath.First())//如果文件虚拟路径被顶级文件夹虚拟路径包含，则需要裁剪该部分
                        {
                            dirFile.AbstractDirPath = string.Join(@"\", dirFileAbstractPath.TakeLast(dirFileAbstractPath.Length - 1));
                        }
                        var fileAbstractDirPath = Path.Combine(topDirPath, dirFile.AbstractDirPath);//文件存储的虚拟路径(文件在目录中，不包含文件名)
                        if (FileOperateHelper.CreateFileDir(fileAbstractDirPath))
                        {
                            //移动文件到虚拟目录对应的真实目录中，同时改名（如果文件已存在则跳过）
                            var fileAbstractPath = Path.Combine(fileAbstractDirPath, dirFile.Name + dirFile.Extension);//虚拟目标文件路径（真实名称+格式后缀）
                            var fileRealPath = Path.Combine(dirFile.DirPath, dirFile.PId.ToString() + dirFile.Extension);//真实原文件路径（GUID名称+格式后缀）
                            var newRealPath = FileOperateHelper.CopyFile(fileRealPath, fileAbstractPath);
                            
                        }
                    }
                }
                var dirPath = FileOperateHelper.GetDirectoryPhysicalPath(topDirPath);
                return Json(dirPath.Replace(@"\","/"));
            }
            catch (Exception ex)
            {
#pragma warning disable CS4014
                //拷贝文件失败则把拷贝的垃圾文件全部删除
                FileOperateHelper.TryDeleteDirectory(tempReadingDir, true);//异步删除，无需等待
#pragma warning restore CS4014
                throw ex;
            }
        }

        /// <summary>
        /// 获取所有在指定顶级文件夹下的文件（如果发生映射重复文件也带出来，尽可能带出文件）
        /// </summary>
        /// <param name="topDirGuid"></param>
        /// <returns></returns>
        private async Task<List<CompleteFileResult>> GetAllFileInDir(string topDirGuid)
        {
            var dirReflectFiles = await _reflectfileService.FindAll(x => x.AbstractTopDirGuid == topDirGuid);//映射文件
            var dirReflectFilesPIDs = dirReflectFiles.Select(x => x.PId);//映射文件对应的原文件GUID集合
            var sourceFiles = await _fileService.FindAll(x => dirReflectFilesPIDs.Contains(x.Id));//映射文件对应的原文件集合
            return CompleteFileHelper.CompletingFileInfo(dirReflectFiles, sourceFiles);
        }
    }
}
